//
//  UIImageView+FGCornerRadius.m
//  FGTool
//
//  Created by Henry on 2021/6/23.
//

#import "UIImageView+FGCornerRadius.h"
#import <objc/runtime.h>

const char fg_kProcessedImage;

@interface UIImageView ()

@property (assign, nonatomic) CGFloat FGRadius;
@property (assign, nonatomic) UIRectCorner FGRoundCorner;
@property (assign, nonatomic) CGFloat FGBorderWidth;
@property (strong, nonatomic) UIColor *FGBorderColor;
@property (assign, nonatomic) BOOL FGHadAddObserver;
@property (assign, nonatomic) BOOL FGIsRounding;

@end

@implementation UIImageView (FGCornerRadius)

- (instancetype)initWithCornerRadius:(CGFloat)cornerRadius rectCorner:(UIRectCorner)rectCorner {
    self = [super init];
    if (self) {
        [self fg_cornerRadius:cornerRadius rectCorner:rectCorner];
    }
    return self;
}

- (instancetype)initWithCornerRadius:(CGFloat)cornerRadius {
    self = [super init];
    if (self) {
        [self fg_cornerRadius:cornerRadius];
    }
    return self;
}

- (instancetype)initWithRoundRect {
    self = [super init];
    if (self) {
        [self fg_cornerRadiusRoundRect];
    }
    return self;
}

- (void)fg_cornerRadius:(CGFloat)cornerRadius rectCorner:(UIRectCorner)rectCorner {
    self.FGRadius = cornerRadius;
    self.FGRoundCorner = rectCorner;
    self.FGIsRounding = NO;
    if (!self.FGHadAddObserver) {
        [[self class] swizzleDealloc];
        [self addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
        self.FGHadAddObserver = YES;
    }
    //Xcode 8 xib 删除了控件的Frame信息，需要主动创造
    [self layoutIfNeeded];
}

- (void)fg_cornerRadius:(CGFloat)cornerRadius {
    if (cornerRadius < 0) {
        [self fg_cornerRadiusRoundRect];
        return;
    }
    [self fg_cornerRadius:cornerRadius rectCorner:UIRectCornerAllCorners];
}

- (void)fg_cornerRadiusRoundRect {
    self.FGIsRounding = YES;
    if (!self.FGHadAddObserver) {
        [[self class] swizzleDealloc];
        [self addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
        self.FGHadAddObserver = YES;
    }
    //Xcode 8 xib 删除了控件的Frame信息，需要主动创造
    [self layoutIfNeeded];
    
}

- (void)fg_borderWidth:(CGFloat)width color:(UIColor *)color {
    self.FGBorderWidth = width;
    self.FGBorderColor = color;
}

#pragma mark - Kernel

- (void)fg_cornerRadiusWithImage:(UIImage *)image cornerRadius:(CGFloat)cornerRadius rectCorner:(UIRectCorner)rectCorner {
    CGSize size = self.bounds.size;
    CGFloat scale = [UIScreen mainScreen].scale;
    CGSize cornerRadii = CGSizeMake(cornerRadius, cornerRadius);
    
    UIGraphicsBeginImageContextWithOptions(size, NO, scale);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    if (nil == currentContext) {
        return;
    }
    UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCorner cornerRadii:cornerRadii];
    [cornerPath addClip];
    [self.layer renderInContext:currentContext];
    [self drawBorder:cornerPath];
    UIImage *processedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    if (processedImage) {
        objc_setAssociatedObject(processedImage, &fg_kProcessedImage, @(1), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    self.image = processedImage;
}

- (void)fg_cornerRadiusWithImage:(UIImage *)image cornerRadius:(CGFloat)cornerRadius rectCorner:(UIRectCorner)rectCorner backgroundColor:(UIColor *)backgroundColor {
    CGSize size = self.bounds.size;
    CGFloat scale = [UIScreen mainScreen].scale;
    CGSize cornerRadii = CGSizeMake(cornerRadius, cornerRadius);
    
    UIGraphicsBeginImageContextWithOptions(size, YES, scale);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    if (nil == currentContext) {
        return;
    }
    UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCorner cornerRadii:cornerRadii];
    UIBezierPath *backgroundRect = [UIBezierPath bezierPathWithRect:self.bounds];
    [backgroundColor setFill];
    [backgroundRect fill];
    [cornerPath addClip];
    [self.layer renderInContext:currentContext];
    [self drawBorder:cornerPath];
    UIImage *processedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    if (processedImage) {
        objc_setAssociatedObject(processedImage, &fg_kProcessedImage, @(1), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    self.image = processedImage;
}

#pragma mark - Private

- (void)drawBorder:(UIBezierPath *)path {
    if (0 != self.FGBorderWidth && nil != self.FGBorderColor) {
        [path setLineWidth:2 * self.FGBorderWidth];
        [self.FGBorderColor setStroke];
        [path stroke];
    }
}

- (void)fg_dealloc {
    if (self.FGHadAddObserver) {
        [self removeObserver:self forKeyPath:@"image"];
    }
    [self fg_dealloc];
}

- (void)validateFrame {
    if (self.frame.size.width == 0) {
        [self.class swizzleLayoutSubviews];
    }
}

+ (void)swizzleMethod:(SEL)oneSel anotherMethod:(SEL)anotherSel {
    Method oneMethod = class_getInstanceMethod(self, oneSel);
    Method anotherMethod = class_getInstanceMethod(self, anotherSel);
    method_exchangeImplementations(oneMethod, anotherMethod);
}

+ (void)swizzleDealloc {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleMethod:NSSelectorFromString(@"dealloc") anotherMethod:@selector(fg_dealloc)];
    });
}

+ (void)swizzleLayoutSubviews {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleMethod:@selector(layoutSubviews) anotherMethod:@selector(fg_LayoutSubviews)];
    });
}

- (void)fg_LayoutSubviews {
    [self fg_LayoutSubviews];
    if (self.FGIsRounding) {
        [self fg_cornerRadiusWithImage:self.image cornerRadius:self.frame.size.width/2 rectCorner:UIRectCornerAllCorners];
    } else if (0 != self.FGRadius && 0 != self.FGRoundCorner && nil != self.image) {
        [self fg_cornerRadiusWithImage:self.image cornerRadius:self.FGRadius rectCorner:self.FGRoundCorner];
    }
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"image"]) {
        UIImage *newImage = change[NSKeyValueChangeNewKey];
        if ([newImage isMemberOfClass:[NSNull class]]) {
            return;
        } else if ([objc_getAssociatedObject(newImage, &fg_kProcessedImage) intValue] == 1) {
            return;
        }
        [self validateFrame];
        if (self.FGIsRounding) {
            [self fg_cornerRadiusWithImage:newImage cornerRadius:self.frame.size.width/2 rectCorner:UIRectCornerAllCorners];
        } else if (0 != self.FGRadius && 0 != self.FGRoundCorner && nil != self.image) {
            [self fg_cornerRadiusWithImage:newImage cornerRadius:self.FGRadius rectCorner:self.FGRoundCorner];
        }
    }
}

#pragma mark - Property

- (CGFloat)FGBorderWidth {
    return [objc_getAssociatedObject(self, _cmd) floatValue];
}

- (void)setFGBorderWidth:(CGFloat)FGBorderWidth {
    objc_setAssociatedObject(self, @selector(FGBorderWidth), @(FGBorderWidth), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIColor *)FGBorderColor {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setFGBorderColor:(UIColor *)FGBorderColor {
    objc_setAssociatedObject(self, @selector(FGBorderColor), FGBorderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)FGHadAddObserver {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setFGHadAddObserver:(BOOL)FGHadAddObserver {
    objc_setAssociatedObject(self, @selector(FGHadAddObserver), @(FGHadAddObserver), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)FGIsRounding {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setFGIsRounding:(BOOL)FGIsRounding {
    objc_setAssociatedObject(self, @selector(FGIsRounding), @(FGIsRounding), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIRectCorner)FGRoundCorner {
    return [objc_getAssociatedObject(self, _cmd) unsignedLongValue];
}
    
- (void)setFGRoundCorner:(UIRectCorner)FGRoundCorner {
    objc_setAssociatedObject(self, @selector(FGRoundCorner), @(FGRoundCorner), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (CGFloat)FGRadius {
    return [objc_getAssociatedObject(self, _cmd) floatValue];
}

- (void)setFGRadius:(CGFloat)FGRadius {
    objc_setAssociatedObject(self, @selector(FGRadius), @(FGRadius), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
